﻿#region Includes

using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Globalization;
using System.Data.Linq;

#endregion

namespace ArtheaEngine
{
    public static class ArtheaHelper
    {
        public const short Immortal = 201;
        public const short MaxLevel = 201;

        static ArtheaHelper()
        {
            Randomizer = new Random();
        }


        public static void Set<T,J>(this EntityRef<T> obj, T value, J baseObj, string collectionName) where T : class
        {
            T priorObj = obj.Entity;
            T newObj = value;
            ICollection<J> collection = typeof(T).GetProperty(collectionName).GetValue(value, null) as ICollection<J>;

            if (newObj != priorObj)
            {
                // remove this book from our prior category's list of books
                obj.Entity = null;
                if (priorObj != null)
                {
                    collection.Remove(baseObj);
                }

                // set category to the new value
                obj.Entity = newObj;

                // add this book to the new category's list of books
                if (newObj != null)
                {
                    collection.Add(baseObj);
                }
            }
        }


        /// <summary>
        /// Puts the first letter in upper case and the rest in lower case
        /// </summary>
        /// <returns>a captialized string</returns>
        public static string Capitalize(this string mString, bool tolower)
        {
            if (string.IsNullOrEmpty(mString))
            {
                return mString;
            }

            var firstLetter = !char.IsUpper(mString[0]) ? char.ToUpper(mString[0]) : mString[0];

            if (tolower)
                return firstLetter + mString.Substring(1).ToLower();


            return firstLetter + mString.Substring(1);
        }

        public static string Capitalize(this string mString)
        {
            return mString.Capitalize(true);
        }

        public static bool IsPrefixOf(this string mString, string value, bool ignoreCase)
        {
            return value.StartsWith(mString, ignoreCase, CultureInfo.CurrentCulture);
        }

        public static bool IsPrefixOf(this string mString, string value)
        {
            return IsPrefixOf(mString, value, true);
        }

        public static void CloneObj(object newObject, object thisObj)
        {
            var fields = newObject.GetType().GetFields();

            var i = 0;

            foreach (var fi in thisObj.GetType().GetFields())
            {
                //We query if the fiels support the ICloneable interface.

                var cloneType = fi.FieldType.
                    GetInterface("ICloneable", true);

                if (cloneType != null)
                {
                    //Getting the ICloneable interface from the object.

                    var clone = (ICloneable) fi.GetValue(thisObj);

                    //We use the clone method to set the new value to the field.

                    fields[i].SetValue(newObject, clone.Clone());
                }
                else
                {
                    // If the field doesn't support the ICloneable 

                    // interface then just set it.

                    fields[i].SetValue(newObject, fi.GetValue(thisObj));
                }

                //Now we check if the object support the 

                //IEnumerable interface, so if it does

                //we need to enumerate all its items and check if 

                //they support the ICloneable interface.

                var enumerableType = fi.FieldType.GetInterface
                    ("IEnumerable", true);
                if (enumerableType != null)
                {
                    //Get the IEnumerable interface from the field.

                    var e = (IEnumerable) fi.GetValue(thisObj);

                    //This version support the IList and the 

                    //IDictionary interfaces to iterate on collections.

                    var listType = fields[i].FieldType.GetInterface
                        ("ICollection", true);
                    var dicType = fields[i].FieldType.GetInterface
                        ("IDictionary", true);

                    var j = 0;
                    if (listType != null)
                    {
                        //Getting the IList interface.

                        var list = (IList) fields[i].GetValue(newObject);

                        foreach (var obj in e)
                        {
                            //Checking to see if the current item 

                            //support the ICloneable interface.

                            cloneType = obj.GetType().
                                GetInterface("ICloneable", true);

                            if (cloneType != null)
                            {
                                //If it does support the ICloneable interface, 

                                //we use it to set the clone of

                                //the object in the list.

                                var clone = (ICloneable) obj;

                                list[j] = clone.Clone();
                            }

                            //NOTE: If the item in the list is not 

                            //support the ICloneable interface then in the 

                            //cloned list this item will be the same 

                            //item as in the original list

                            //(as long as this type is a reference type).


                            j++;
                        }
                    }
                    else if (dicType != null)
                    {
                        //Getting the dictionary interface.

                        var dic = (IDictionary) fields[i].
                                                    GetValue(newObject);
                        j = 0;

                        foreach (DictionaryEntry de in e)
                        {
                            //Checking to see if the item 

                            //support the ICloneable interface.

                            cloneType = de.Value.GetType().
                                GetInterface("ICloneable", true);

                            if (cloneType != null)
                            {
                                var clone = (ICloneable) de.Value;

                                dic[de.Key] = clone.Clone();
                            }
                            j++;
                        }
                    }
                }
                i++;
            }
        }

        public static bool DeepCompare(this byte[] orig, byte[] passwd)
        {
            if (orig.Length != passwd.Length)
                return false;

            for (var i = 0; i < orig.Length; i++)
            {
                if (orig[i] != passwd[i])
                {
                    return false;
                }
            }
            return true;
        }

        public static int DisplayLength(this string str)
        {
            return str.StripColor().Length;
        }

        public static T Get<T>(this IEnumerable<T> list, Predicate<T> pred)
        {
            foreach (var t in list)
            {
                if (pred(t))
                    return t;
            }
            return default(T);
        }

        /// <summary>
        /// Gets the length of a number of lines.
        /// </summary>
        /// <param name="str">The STR.</param>
        /// <param name="count">The number of lines to count.</param>
        /// <returns></returns>
        public static int GetLineCountLength(this string str, int count)
        {
            var length = str.IndexOf(Environment.NewLine);
            var i = 0;
            var point = -1;

            while (point < str.Length)
            {
                if (++i >= count)
                    break;

                point = length + Environment.NewLine.Length;

                length = str.IndexOf(Environment.NewLine, point);

                if (length == -1)
                {
                    point += str.Length - point;
                    break;
                }
            }

            return point;
        }

        /// <summary>
        /// Gets the lines.
        /// </summary>
        /// <returns></returns>
        public static string[] GetLines(this string mString)
        {
            if (mString == null) return new string[0];
            return mString.Split(new[] {Environment.NewLine}, StringSplitOptions.None);
        }

        /// <summary>
        /// Finds an argument prefixed by a multiplier (7*argument)
        /// </summary>
        /// <param name="arg">The arg.</param>
        /// <param name="str">the string to operate on</param>
        /// <returns></returns>
        public static int GetMultiArg(this string str, out string arg)
        {
            return str.GetNumberedArg(out arg, '*');
        }

        /// <summary>
        /// Finds an argument prefixed by an index (7.argument)
        /// </summary>
        /// <param name="str">the string to operate on</param>
        /// <param name="arg">The arg.</param>
        /// <returns></returns>
        public static int GetNumberedArg(this string str, out string arg)
        {
            return str.GetNumberedArg(out arg, '.');
        }

        private static int GetNumberedArg(this string mString, out string arg, char c)
        {
            var index = mString.IndexOf(c);

            if (index == -1)
            {
                arg = mString;
                return 1;
            }

            int number;

            int.TryParse(mString.Substring(0, index), out number);

            arg = mString.Substring(index + 1);

            return number;
        }

        public static bool HasWord(this string value, string word)
        {
            var arg = new Argument(value);

            while (!arg.IsEmpty())
            {
                var check = arg.Next();

                if (check.Equals(word, StringComparison.CurrentCultureIgnoreCase))
                    return true;
            }

            return false;
        }

        public static bool IsEmail(string inputEmail)
        {
            inputEmail = inputEmail ?? "";
            const string strRegex = @"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}" +
                                    @"\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\" +
                                    @".)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$";
            var re = new Regex(strRegex);
            return re.IsMatch(inputEmail);
        }

        /// <summary>
        /// Determines whether the specified STR has a line.
        /// </summary>
        /// <param name="str">The STR.</param>
        /// <returns>
        /// 	<c>true</c> if the specified STR has a line; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsLine(this string str)
        {
            return str.Length > 0 && str.EndsWith(Environment.NewLine);
        }

        /// <summary>
        /// Checks if a string is a number
        /// </summary>
        /// <returns>true if the string is a number</returns>
        public static bool IsNumber(this string mString)
        {
            if (string.IsNullOrEmpty(mString))
                return false;

            bool foundPoint = false;
            bool foundNeg = false;
            bool foundNum = false;

            for (var ch = mString.GetEnumerator(); ch.MoveNext();)
            {
                if (ch.Current == '.')
                {
                    if (foundPoint) return false;

                    foundPoint = true;
                    continue;
                }

                if (ch.Current == '-')
                {
                    if (foundNeg || foundNum) return false;

                    foundNeg = true;
                    continue;
                }

                if (!char.IsDigit(ch.Current))
                    return false;

                foundNum = true;
            }
            return true;
        }

        /// <summary>
        /// Joins the specified separator.
        /// </summary>
        /// <param name="separator">The separator.</param>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        public static string JoinWith(this String[] value, string separator)
        {
            var newValue = new string[value.Length];
            value.CopyTo(newValue, 0);
            return String.Join(separator, newValue);
        }


        /// <summary>
        /// Makes the readable.
        /// </summary>
        /// <param name="str">The STR.</param>
        /// <returns></returns>
        public static string CamelToHuman(this string str)
        {
            var buf = new StringBuilder();
            var c = str.GetEnumerator();

            if (!c.MoveNext())
                return str;

            buf.Append(char.ToUpper(c.Current));

            while (c.MoveNext())
            {
                if (char.IsUpper(c.Current))
                {
                    buf.Append(" ");
                }
                buf.Append(c.Current);
            }

            return buf.ToString();
        }


        public static string StripColor(this string mString)
        {
            return Regex.Replace(mString, Color.Code +
                                          "[!@#$%]*[GRCBYMDWNgrcbymdwxn?*" + Color.Code + "]", string.Empty);
        }

        public static Random Randomizer { get; private set; }
    }
}